/********************************************************************
   Author: Minh B. Do (Arizona State University - binhminh@asu.edu)
 *********************************************************************/
package edu.asu.sapa.ground;

import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.Map.Entry;

import edu.asu.sapa.ground.update.Set;
import edu.asu.sapa.ground.update.Test;

/**
 * FluentDB: Metric Resource Database. This class manages the set of values for
 * all continuous functions. All things related to the metric map are from June
 * 12, 2004 and done by J. Benton.
 * 
 * @author Binh Minh Do
 * @author J. Benton
 */
public class FluentDB implements Cloneable {
	public HashMap<Integer, Float> valueMap;

	public FluentDB() {
		valueMap = new HashMap<Integer, Float>(0);
	}

	public FluentDB(FluentDB mr) {
		valueMap = new HashMap<Integer, Float>(mr.valueMap);
	}

	public Object clone() {
		FluentDB o = null;
		try {
			o = (FluentDB) super.clone();
		} catch (CloneNotSupportedException e) {
			// assert false;
		}
		o.valueMap = (HashMap<Integer, Float>) o.valueMap.clone();
		return o;
	}

	public final int size() {
		return valueMap.size();
	}

	/** Get the value of a particular function */
	public float get(int id) {
		Float f = valueMap.get(id);
		if (f == null)
			return Float.NaN;
		return f;
	}

	public float put(int id, Float value) {
		float k = value;
		if (k != k)
			return remove(id);
		Float f = valueMap.put(id, value);
		if (f == null)
			return Float.NaN;
		return f;
	}
	
	public float put(int id, float value) {
		if (value != value)
			return remove(id);
		Float f = valueMap.put(id, value);
		if (f == null)
			return Float.NaN;
		return f;
	}

	public void set(int id, float value) {
		if (value != value)
			valueMap.remove(id);
		else
			valueMap.put(id,value);
	}


	// because the below method does a useless comparison if the return value isn't needed
	public void delete(int id) {
		valueMap.remove(id);
	}
	
	public float remove(int id) {
		Float f = valueMap.remove(id);
		if (f == null)
			return Float.NaN;
		return f;
	}
	
	/**
	 * Update a function value according to the op and the indicated
	 * assignment/increment/decreasement value oper = 0 (=); 1: (-=); 2: (+=);
	 * 3: (*=): 4: (/=)
	 */
	public boolean update(int op, Integer id, float value) {
		if (op == 0) {
			valueMap.put(id, value);
			return true;
		}

		Float f = valueMap.get(id);
		if (f == null)
			return false;

		float v = f;
		
		switch (op) {
		case 1:
			v += value;
			break;
		case 2:
			v -= value;
			break;
		case 3:
			v *= value;
			break;
		case 4:
			v /= value;
			break;
		default:
			valueMap.remove(id);
			return false;
		}

		valueMap.put(id, v);
		return true;
	}
	
	public boolean update(int op, Integer id, Float value) {
		if (op == 0) {
			valueMap.put(id, value);
			return true;
		}

		Float f = valueMap.get(id);
		if (f == null)
			return false;

		float v = f;
		float u = value;
		
		switch (op) {
		case 1:
			v += u;
			break;
		case 2:
			v -= u;
			break;
		case 3:
			v *= u;
			break;
		case 4:
			v /= u;
			break;
		default:
			valueMap.remove(id);
			return false;
		}

		valueMap.put(id, v);
		return true;
	}


	public String toString() {
		String s = "FluentDB:\n";
		for (Entry<Integer, Float> fv : valueMap.entrySet()) {
			s += "\t(:= " + fv.getKey() + " " + fv.getValue() + ")\n";
		}
		return s;
	}

	public float value(int id) {
		Float val = valueMap.get(id);
		if (val == null) return Float.NaN;
		return val;
	}
	
	// FIXME.  Multiplication is not commutative when it overflows.
	@Override
	public int hashCode() {
		int result = 1;
		for(Map.Entry<Integer,Float> e : valueMap.entrySet()) {
			//result = 31 * result + (e.getKey().hashCode() ^ e.getValue().hashCode());
			result *= (e.getKey().hashCode() ^ e.getValue().hashCode());
		}
		return result;
	}
	
}
